#include "klib.h"
#include "cte.h"
#include "sysnum.h"
#include "vme.h"
#include "serial.h"
#include "loader.h"
#include "proc.h"
#include "timer.h"
#include "file.h"

typedef int (*syshandle_t)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);

extern void *syscall_handle[NR_SYS];

void do_syscall(Context *ctx) {
  // TODO: Lab1-5 call specific syscall handle and set ctx register
  int sysnum = ctx -> eax;
  uint32_t arg1 = ctx -> ebx;
  uint32_t arg2 = ctx -> ecx;
  uint32_t arg3 = ctx -> edx;
  uint32_t arg4 = ctx -> esi;
  uint32_t arg5 = ctx -> edi;
  int res;
  if (sysnum < 0 || sysnum >= NR_SYS) {
    res = -1;
  } else {
    res = ((syshandle_t)(syscall_handle[sysnum]))(arg1, arg2, arg3, arg4, arg5);
  }
  ctx->eax = res;
}

int sys_write(int fd, const void *buf, size_t count) {
  // TODO: rewrite me at Lab3-1
  return serial_write(buf, count);
}

int sys_read(int fd, void *buf, size_t count) {
  // TODO: rewrite me at Lab3-1
  return serial_read(buf, count);
}

int sys_brk(void *addr) {
  // TODO: Lab1-5
  //以前我们处理brk系统调用是使用这个函数里的static变量来记录program break，现在有了进程，program break的记录位置改成了当前进程PCB的brk成员，所以这里以前对static变量brk的操作要改成对当前进程PCB的brk操作，注意可以使用proc_curr来获取当前进程的PCB。
  proc_t *current_pcb = proc_curr();
  size_t new_brk = PAGE_UP(addr);
  if (current_pcb->brk == 0) {
    current_pcb->brk = new_brk;
  } else if (new_brk > current_pcb->brk) {
    PD *pgdir = vm_curr();
    vm_map(pgdir, current_pcb->brk, new_brk - current_pcb->brk, 7);
    current_pcb->brk = new_brk;
  } else if (new_brk < current_pcb->brk) {
    // can just do nothing
  }
  return 0;

  /*static size_t brk = 0; // use brk of proc instead of this in Lab2-1
  size_t new_brk = PAGE_UP(addr);
  if (brk == 0) {
    brk = new_brk;
  } else if (new_brk > brk) {
    PD *pgdir = vm_curr();
    vm_map(pgdir, brk, new_brk - brk, 7);
  } else if (new_brk < brk) {
    // can just do nothing
  }
  return 0;*/

}

void sys_sleep(int ticks) {
  int cur_ticks = get_tick();
  /*一开始get_tick获取一下当前系统时间，然后一直等到ticks刻过去即可。
至于怎么等——和前面getchar一样，循环判断现在的系统时间是不是比开始时过去了ticks刻，如果没有，就sti(); hlt(); cli();三连等中断睡大觉就行了。*/
  while (get_tick() - cur_ticks < ticks) {
    // sti(); hlt(); cli();
    proc_yield(); // Lab2-1
  }
  
}

int sys_exec(const char *path, char *const argv[]) {
  
  // TODO: Lab1-5
  PD *new_pgdir = vm_alloc();
  if (new_pgdir == NULL) {
    return -1;
  }

  Context new_ctx;
  
  int ret = load_user(new_pgdir, &new_ctx, path, argv);
  if (ret != 0) {
    //回收分配的页表
    return -1;
  } else {

    //切换页表lab2-1
    proc_t *current_pcb = proc_curr();
    current_pcb->pgdir = new_pgdir;



    // PD *cur_pgdir = vm_curr();
    set_cr3(new_pgdir);
    //回收旧的页目录


    /*AI生成的，不一定有用
    new_ctx.eip = (uint32_t)new_ctx.eip;
    */
    irq_iret(&new_ctx);

    return 0;

  }
  
  
}

int sys_getpid() {
  return proc_curr() -> pid;
}

void sys_yield() {
  proc_yield();
}

int sys_fork() {
  proc_t *child_pcb = proc_alloc();
  if (child_pcb == NULL) {
    return -1;
  }
  proc_copycurr(child_pcb);
  proc_addready(child_pcb);
  // printf("这里！！child_pcb->pid: %d\n", child_pcb->pid);
  return child_pcb->pid;
}

void sys_exit(int status) {
  proc_t* cr_pr = proc_curr();
  proc_makezombie(cr_pr, status);
  asm volatile ("int $0x81");
assert(0);
  while (1) proc_yield();
}

int sys_wait(int *status) {
    proc_t* cr_pr = proc_curr();

  if (cr_pr->child_num == 0) {
    return -1;
  }


  //有信号量之前循环调用
  /*proc_t *zombie = proc_findzombie(cr_pr);
  
  while (zombie == NULL) {
    proc_yield();
    zombie = proc_findzombie(cr_pr);
  }*/

  //使用信号量
  sem_p(&(cr_pr->zombie_sem));
  proc_t *zombie = proc_findzombie(cr_pr);


  if (status != NULL) {
    // zombie->exit_code = *status;
    *status = zombie->exit_code;
  } else {
    proc_yield();
  }

int pid = zombie->pid;

  proc_free(zombie);

  cr_pr->child_num--;

  return pid;


  // if (status != NULL) {
  //   *status = zombie->exit_status;
  // }

  // proc_remove(zombie);
  // return zombie->pid;


  // sys_sleep(250);
  // return 0;
}

int sys_sem_open(int value) {
  proc_t *cr_proc= proc_curr();

  int sem_id = proc_allocusem(cr_proc);
  if (sem_id == -1) {
    return -1;
  }
  usem_t *sem = usem_alloc(value);
  if (sem == NULL) {
    return -1;
  }
 cr_proc->usems[sem_id] = sem;
  return sem_id;
}

int sys_sem_p(int sem_id) {
  proc_t *cr_proc = proc_curr();
  usem_t *usem = proc_getusem(cr_proc, sem_id);

  if (usem == NULL) {
    return -1;
  }
  sem_p(&(usem->sem));
  return 0;
}

int sys_sem_v(int sem_id) {
  proc_t *cr_proc = proc_curr();
  usem_t *usem = proc_getusem(cr_proc, sem_id);

  if (usem == NULL) {
    return -1;
  }
  sem_v(&(usem->sem));
  return 0;
}

int sys_sem_close(int sem_id) {
  proc_t *cr_proc = proc_curr();
  usem_t *usem = proc_getusem(cr_proc, sem_id);

  if (usem == NULL) {
    return -1;
  }
  usem_close(usem);
  cr_proc->usems[sem_id] = NULL;
  return 0;
}

int sys_open(const char *path, int mode) {
  TODO(); // Lab3-1
}

int sys_close(int fd) {
  TODO(); // Lab3-1
}

int sys_dup(int fd) {
  TODO(); // Lab3-1
}

uint32_t sys_lseek(int fd, uint32_t off, int whence) {
  TODO(); // Lab3-1
}

int sys_fstat(int fd, struct stat *st) {
  TODO(); // Lab3-1
}

int sys_chdir(const char *path) {
  TODO(); // Lab3-2
}

int sys_unlink(const char *path) {
  return iremove(path);
}

// optional syscall

void *sys_mmap() {
  TODO();
}

void sys_munmap(void *addr) {
  TODO();
}

int sys_clone(void (*entry)(void*), void *stack, void *arg) {
  TODO();
}

int sys_kill(int pid) {
  TODO();
}

int sys_cv_open() {
  TODO();
}

int sys_cv_wait(int cv_id, int sem_id) {
  TODO();
}

int sys_cv_sig(int cv_id) {
  TODO();
}

int sys_cv_sigall(int cv_id) {
  TODO();
}

int sys_cv_close(int cv_id) {
  TODO();
}

int sys_pipe(int fd[2]) {
  TODO();
}

int sys_link(const char *oldpath, const char *newpath) {
  TODO();
}

int sys_symlink(const char *oldpath, const char *newpath) {
  TODO();
}

void *syscall_handle[NR_SYS] = {
  [SYS_write] = sys_write,
  [SYS_read] = sys_read,
  [SYS_brk] = sys_brk,
  [SYS_sleep] = sys_sleep,
  [SYS_exec] = sys_exec,
  [SYS_getpid] = sys_getpid,
  [SYS_yield] = sys_yield,
  [SYS_fork] = sys_fork,
  [SYS_exit] = sys_exit,
  [SYS_wait] = sys_wait,
  [SYS_sem_open] = sys_sem_open,
  [SYS_sem_p] = sys_sem_p,
  [SYS_sem_v] = sys_sem_v,
  [SYS_sem_close] = sys_sem_close,
  [SYS_open] = sys_open,
  [SYS_close] = sys_close,
  [SYS_dup] = sys_dup,
  [SYS_lseek] = sys_lseek,
  [SYS_fstat] = sys_fstat,
  [SYS_chdir] = sys_chdir,
  [SYS_unlink] = sys_unlink,
  [SYS_mmap] = sys_mmap,
  [SYS_munmap] = sys_munmap,
  [SYS_clone] = sys_clone,
  [SYS_kill] = sys_kill,
  [SYS_cv_open] = sys_cv_open,
  [SYS_cv_wait] = sys_cv_wait,
  [SYS_cv_sig] = sys_cv_sig,
  [SYS_cv_sigall] = sys_cv_sigall,
  [SYS_cv_close] = sys_cv_close,
  [SYS_pipe] = sys_pipe,
  [SYS_link] = sys_link,
  [SYS_symlink] = sys_symlink};
